#include "usart.h"

uint8_t data[1024]  = {0};
uint16_t data_index = 0;

void usart0_init(void) {
    /** step 1 先把GPIO引脚初始化完成 **/
    // 1.1 打开 GPIO的时钟
    rcu_periph_clock_enable(RCU_USART_TX_GPIO_PORT);

    // 1.2 配置GPIO的模式（输入、输出、复用等，选择复用）
    // A9脚 选择复用模式（输入和输出的高低电平将不受我们控制，而是复用的具体功能管控）
    gpio_mode_set(USART_TX_GPIO_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, USART_TX_GPIO_PIN);
    gpio_mode_set(USART_RX_GPIO_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, USART_RX_GPIO_PIN); // 接收PA10初始化

    // 1.3 选择具体复用功能几，USART0_TX功能是复用7
    // A9脚 交由复用功能7控制（USART0_TX电路控制）
    gpio_af_set(USART_TX_GPIO_PORT, USART_TX_GPIO_AF, USART_TX_GPIO_PIN);
    gpio_af_set(USART_RX_GPIO_PORT, USART_RX_GPIO_AF, USART_RX_GPIO_PIN);

    // 1.4 配置推挽输出
    // gpio_output_options_set(USART_TX_GPIO_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_MAX, USART_TX_GPIO_PIN);
    // gpio_output_options_set(USART_TX_GPIO_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_MAX, USART_TX_GPIO_PIN);

    /** step 2 串口相关的配置了 上面选择复用7，让USART0_TX干活，那么USART0_TX也要设置**/

    // 2.1 串口的时钟使能, 整个USART0就接入时钟信号了
    rcu_periph_clock_enable(RCU_USART0);

    // 2.2 串口初始化，恢复默认设置（可选）
    usart_deinit(USART0);

    // 2.3 波特率的设置，一般选择115200（不快不慢，稳定）
    usart_baudrate_set(USART0, USART0_BAUERATE);

    // 2.4 校验位设置（选择无校验位）
    usart_parity_config(USART0, USART0_PARITY);

    // 2.5 设置字宽（发送一轮数据，是几个bit，可选5~8bit），肯定选择8bit
    usart_word_length_set(USART0, USART0_WORD_LENGTH);

    // 2.6 设置停止位宽度，选择1bit宽度
    usart_stop_bit_set(USART0, USART0_STOP_BIT_SET);

    // 2.7 设置先发送二进制低位还是高位
    usart_data_first_config(USART0, USART0_FIRST_CONFIG);

    // 2.8 开启发送功能（串口给了时钟仅仅是有时钟信号，串口内部发送和接收有控制单元，需要打开）
    usart_transmit_config(USART0, USART_TRANSMIT_ENABLE);

    // 2.9 开启接收功能
    usart_receive_config(USART0, USART_RECEIVE_ENABLE);

    // 2.10 开启中断功能（设置串口的中断优先级）
    // 参数1，是被设置的，选择USART0_IRQn，通过查阅源码得知，串口0需要写USART0_IRQn
    // 参数2，抢占优先级（绝对优先级，越小越高）
    // 参数3，响应优先级，在抢占优先级相同的时候，响应优先级有限
    nvic_irq_enable(USART0_IRQn, 2, 2);

    // 2.11 开启RBNE中断（让系统对RBNE产生反应，产生RBNE后会调用我们的中断函数）
    // RBNE receive buffer not empty 接收缓冲区非空 中断
    usart_interrupt_enable(USART0, USART_INT_RBNE); // 当接收缓冲区非空，会自动调用中断函数
    // 2.12 开启IDLE中断，IDLE空闲，表示数据传输完成
    usart_interrupt_enable(USART0, USART_INT_IDLE); // 当接收空闲（没数据了），会自动调用中断函数
    // 上面2行代码，表示让芯片 理会 RBNE和IDLE，理会表示出现RBNE IDLE会自动调用中断函数

    // 2.13 打开串口（串口总开关，不要和上面的时钟混淆，上面是给串口接入时钟，以及打开发送开关，这个是总开关）
    usart_enable(USART0);
}

// 一定一定一定，是这个名字，只有这个名字，才会在出现RBNE IDLE的时候，被自动调用
void USART0_IRQHandler(void) {
    /* 这个函数会被2个中断自动调用，RBNE和IDLE，我们需要if判断是哪个中断让这个函数跑起来的 */

    // 进入这个if，表示RBNE中断(非空->读数据)
    if (usart_interrupt_flag_get(USART0, USART_INT_FLAG_RBNE) == 1) {
        // 清理标记
        usart_interrupt_flag_clear(USART0, USART_INT_FLAG_RBNE); // 恢复RBNE标志为0

        // usart_data_receive从数据缓冲寄存器读取数据，读完后会清空寄存器
        data[data_index] = (uint8_t)usart_data_receive(USART0);
        data_index++;
    }
    // 进入这个if，表示IDLE中断(空闲->读取完成，添加'\0'并重置data_index为0)
    if (usart_interrupt_flag_get(USART0, USART_INT_FLAG_IDLE) == 1) {
        // usart_interrupt_flag_clear(USART0, USART_INT_FLAG_IDLE);    // 恢复IDLE标志为0

        // 读取一下缓冲区就可以清除IDLE标志
        usart_data_receive(USART0);

        data[data_index] = '\0';
        data_index       = 0;
    }
}

void send_byte(uint8_t data) {
    usart_data_transmit(USART0, data);
    while (0 == usart_flag_get(USART0, USART_FLAG_TBE));
}

int fputc(int ch, FILE* f) {
    send_byte((uint8_t)ch);
    return ch;
}
